#pragma once

#include <atomic>

#include "util/pool/pool.h"
#include "util/radix_tree/radix_tree.h"

namespace hawking {
namespace indexlib {

template<class T>
class TypedSliceList {
public:
    TypedSliceList(uint32_t slot_num, uint32_t slice_length, Pool* pool) : size_(0) {
        void* buffer = pool->Allocate(sizeof(RadixTree));
        data_ = new (buffer) RadixTree(slot_num, slice_length, pool, sizeof(T));
    }
    ~TypedSliceList() = default;

    void PushBack(const T& value) {
        T* data = (T*)data_->OccupyOneItem();
        *data = value;
        size_.fetch_add(1, std::memory_order_relaxed);
    }

    void Update(uint64_t index, const T& value) {
        if (index >= Size()) {
            return;
        }

        T* data = (T*)data_->Search(index * sizeof(T));
        *data = value;
    }

    // careful use
    T& operator[](uint64_t index) {
        T* buffer = (T*)data_->Search(index * sizeof(T));
        return *buffer;
    }

    void Read(uint64_t index, T*& buffer) const {
        if (index >= Size()) {
            return;
        }

        buffer = (T*)data_->Search(index * sizeof(T));
    }

    void Read(uint64_t index, T& value) const {
        T* buffer = nullptr;
        Read(index, buffer);
        if (buffer) {
            value = *buffer;
        }
    }

    uint32_t GetSliceSize(uint32_t slice_id) const {
        return data_->GetSliceSize(slice_id);
    }

    uint32_t GetSliceNum() const {
        return data_->GetSliceNum();
    }

    uint8_t* GetSlice(uint32_t slice_id, uint32_t* slice_length) const {
        *slice_length = GetSliceSize(slice_id);
        return data_->GetSlice(slice_id);
    }

    uint64_t Size() const {
        return size_.load(std::memory_order_relaxed);
    }

private:
    RadixTree* data_ = nullptr;
    std::atomic<uint64_t> size_;
};

}
}
